

#include "stdafx.h"
#include "pf_dimension.h"
#include "pf_solid.h"
#include "pf_text.h"
#include "pf_units.h"

/**
 * Constructor.
 */
PF_Dimension::PF_Dimension(PF_Container* parent,
                           const PF_DimensionAttribute& d)
        : PF_Container(parent), data(d) {
}



PF_Vector PF_Dimension::getNearestRef(const PF_Vector& coord,
                                      double* dist) {

    return PF_Entity::getNearestRef(coord, dist);
}


PF_Vector PF_Dimension::getNearestSelectedRef(const PF_Vector& coord,
        double* dist) {

    return PF_Entity::getNearestSelectedRef(coord, dist);
}



/**
 * @return Dimension text. Either a text the user defined or 
 *         the measured text.
 *
 * @param resolve false: return plain value. true: return measured 
 *      label if appropriate.
 * @see getMeasuredLabel
 */
QString PF_Dimension::getLabel(bool resolve) {
	if (!resolve) {
		return data.text;
	}

    QString ret="";

    // One space suppresses the text:
    if (data.text==" ") {
        ret = "";
    }

    // No text prints actual measurement:
    else if (data.text=="") {
        ret = getMeasuredLabel();
    }

    // Others print the text (<> is replaced by the measurement)
    else {
        ret = data.text;
        ret = ret.replace(QString("<>"), getMeasuredLabel());
    }

    return ret;
}


/**
 * Sets a new text for the label.
 */
void PF_Dimension::setLabel(const QString& l) {
	data.text = l;
}



/**
 * Creates a dimensioning line (line with one, two or no arrows and a text).
 *
 * @param forceAutoText Automatically reposition the text label.
 */
void PF_Dimension::updateCreateDimensionLine(const PF_Vector& p1,
        const PF_Vector& p2, bool arrow1, bool arrow2, bool forceAutoText) {

    // text height (DIMTXT)
    double dimtxt = getTextHeight();
    // text distance to line (DIMGAP)
    double dimgap = getDimensionLineGap();

    // length of dimension line:
    double distance = p1.distanceTo(p2);

    // do we have to put the arrows outside of the line?
    bool outsideArrows = (distance<getArrowSize()*2.5);

    // arrow angles:
    double arrowAngle1, arrowAngle2;

    // Create dimension line:
    PF_Line* dimensionLine = new PF_Line(this, PF_LineAttribute(p1, p2));
    dimensionLine->setPen(PF_Pen(LX_Define::FlagInvalid));
    dimensionLine->setLayer(NULL);
    addEntity(dimensionLine);

    if (outsideArrows==false) {
        arrowAngle1 = dimensionLine->getAngle2();
        arrowAngle2 = dimensionLine->getAngle1();
    } else {
        arrowAngle1 = dimensionLine->getAngle1();
        arrowAngle2 = dimensionLine->getAngle2();
       
        // extend dimension line outside arrows
        PF_Vector dir;
        dir.setPolar(getArrowSize()*2, arrowAngle2);
        dimensionLine->setStartpoint(p1 + dir);
        dimensionLine->setEndpoint(p2 - dir);
    }

    // Arrows:
    PF_SolidAttribute sd;
    PF_Solid* arrow;

    if (arrow1) {
        // arrow 1
        arrow = new PF_Solid(this, sd);
        arrow->shapeArrow(p1,
                          arrowAngle1,
                          getArrowSize());
        arrow->setPen(PF_Pen(LX_Define::FlagInvalid));
        arrow->setLayer(NULL);
        addEntity(arrow);
    }

    if (arrow2) {
        // arrow 2:
        arrow = new PF_Solid(this, sd);
        arrow->shapeArrow(p2,
                          arrowAngle2,
                          getArrowSize());
        arrow->setPen(PF_Pen(LX_Define::FlagInvalid));
        arrow->setLayer(NULL);
        addEntity(arrow);
    }

    // Text label:
    PF_TextAttribute textData;
    PF_Vector textPos;

	double dimAngle1 = dimensionLine->getAngle1();
	double textAngle;
	bool corrected=false;
	textAngle = PF_Math::makeAngleReadable(dimAngle1, true, &corrected);

    if (data.middleOfText.valid && !forceAutoText) {
        textPos = data.middleOfText;
    } else {
        textPos = dimensionLine->getMiddlepoint();

        PF_Vector distV;

        // rotate text so it's readable from the bottom or right (ISO)
        // quadrant 1 & 4
        if (corrected) {
            distV.setPolar(dimgap + dimtxt/2.0, dimAngle1-M_PI/2.0);
        } else {
            distV.setPolar(dimgap + dimtxt/2.0, dimAngle1+M_PI/2.0);
        }

        // move text away from dimension line:
        textPos+=distV;
        //// the next update should still be able to adjust this
        ////   auto text position. leave it invalid
		data.middleOfText = textPos;
    }

    textData = PF_TextAttribute(textPos,
                           dimtxt, 30.0,
                           LX_Define::VAlignMiddle,
                           LX_Define::HAlignCenter,
                           LX_Define::LeftToRight,
                           LX_Define::Exact,
                           1.0,
                           getLabel(),
                           "standard",
                           textAngle);

    PF_Text* text = new PF_Text(this, textData);

    // move text to the side:
    PF_Vector distH;
    if (text->getUsedTextWidth()>distance) {
        distH.setPolar(text->getUsedTextWidth()/2.0
                       +distance/2.0+dimgap, textAngle);
        text->move(distH);
    }
    text->setPen(PF_Pen(LX_Define::FlagInvalid));
    text->setLayer(NULL);
    addEntity(text);
}



/**
 * @return arrow size in drawing units.
 */
double PF_Dimension::getArrowSize() {
    return getGraphicVariable("$DIMASZ", 2.5, 40);
}



/**
 * @return extension line overlength in drawing units.
 */
double PF_Dimension::getExtensionLineExtension() {
    return getGraphicVariable("$DIMEXE", 1.25, 40);
}



/**
 * @return extension line offset from entities in drawing units.
 */
double PF_Dimension::getExtensionLineOffset() {
    return getGraphicVariable("$DIMEXO", 0.625, 40);
}



/**
 * @return extension line gap to text in drawing units.
 */
double PF_Dimension::getDimensionLineGap() {
    return getGraphicVariable("$DIMGAP", 0.625, 40);
}



/**
 * @return Dimension lables text height.
 */
double PF_Dimension::getTextHeight() {
    return getGraphicVariable("$DIMTXT", 2.5, 40);
}



/**
 * @return the given graphic variable or the default value given in mm 
 * converted to the graphic unit.
 * If the variable is not found it is added with the given default
 * value converted to the local unit.
 */
double PF_Dimension::getGraphicVariable(const QString& key, double defMM,
                                        int code) {

    double v = getGraphicVariableDouble(key, LX_DOUBLEMIN);
    if (v<=LX_DOUBLEMIN) {
        addGraphicVariable(
            key,
            PF_Units::convert(defMM, LX_Define::Millimeter, getGraphicUnit()),
            code);
        v = getGraphicVariableDouble(key, 1.0);
    }

    return v;
}




void PF_Dimension::move(PF_Vector offset) {
    data.definitionPoint.move(offset);
    data.middleOfText.move(offset);
}



void PF_Dimension::rotate(PF_Vector center, double angle) {
    data.definitionPoint.rotate(center, angle);
    data.middleOfText.rotate(center, angle);
    data.angle = PF_Math::correctAngle(data.angle+angle);
}



void PF_Dimension::scale(PF_Vector center, PF_Vector factor) {
    data.definitionPoint.scale(center, factor);
    data.middleOfText.scale(center, factor);
}



void PF_Dimension::mirror(PF_Vector axisPoint1, PF_Vector axisPoint2) {
    data.definitionPoint.mirror(axisPoint1, axisPoint2);
    data.middleOfText.mirror(axisPoint1, axisPoint2);
}

// EOF
